Skip to content

feat(questionnaires): F8.1 admin analytics dashboards#53

Merged
JohnD-EE merged 1 commit into
mainfrom
feat/F8.1-admin-analytics-dashboards
Jun 8, 2026
Merged

feat(questionnaires): F8.1 admin analytics dashboards#53
JohnD-EE merged 1 commit into
mainfrom
feat/F8.1-admin-analytics-dashboards

Conversation

@JohnD-EE

@JohnD-EE JohnD-EE commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

F8.1 — Admin analytics dashboards

Opens P8. Adds the admin's read-side view of completed-session data, scoped to a questionnaire version: per-question answer distributions, an invitation completion funnel, and per-version cost actuals — with tag-aware filtering. Read-only, no new models, no migration.

Spec: .context/app/planning/development-plan.md → F8.1. Tracker: .context/app/planning/features/f8.1.md.

What's included

  • Core aggregators (lib/app/questionnaire/analytics/, pure with Prisma at the seam): distributions, funnel, cost, plus a shared query schema + scope resolver that reuses the platform date-range helpers.
  • Three GET routes under …/versions/[vid]/analytics/{distributions,funnel,cost} — admin-only, master-flag-gated, version-scoped (404 on a cross-questionnaire vid).
  • Version-scoped Analytics tab (/admin/questionnaires/[id]/analytics) + client UI: shared filter (date window + tag chips), tabbed distribution / funnel / cost panels. recharts for the cost trend; CSS bars elsewhere; reuses TagChip and FieldHelp.

Key decisions

  • Cost source = AiCostLog, made attributable. Standardised the four session-bound capabilities on the canonical metadata key appQuestionnaireSessionId (runtime spend); design-time spend is read via metadata.versionId (already stamped by evaluate-structure). The two key sets are disjoint, so runtime + design-time never double-count. AppQuestionnaireTurn.costUsd is untouched (it stays the F6.3 budget basis). No migration — AiCostLog.metadata is already Json.
  • Aggregate-only / PII-safe by design. Free-text answer values are never surfaced (only response rate, confidence, provenance), leaving full anonymous-mode hardening to F8.3.
  • Funnel from session reality, not invitation status. Invited/opened come from invitation timestamps; started/completed are derived from real sessions matched to invited respondents by userId. Anonymous (un-invited) sessions are reported separately.

Testing

  • 66 tests: 45 unit (aggregator math, per-type distributions incl. free-text suppression, funnel staging + drop-off + anonymous separation, cost runtime/design split + coercion/fallback edge branches) and 21 route integration tests (flag gate, 401/403, version-scope 404, query validation 400 + error envelope, 200 shape, scope-passing).
  • Full suite green (21971+ passing); npm run validate clean.

Gates run before opening

  • /pre-pr — type-check / lint / format / tests pass; changed-source coverage ≥80%; anti-pattern scan clean; CHANGELOG correctly N/A (app-tier, not platform surface).
  • /test-review — all findings ≥80 addressed.
  • /security-review — no HIGH/MEDIUM findings (raw SQL fully parameterized; auth + version-scoping enforced; aggregate-only output).

Out of scope (later P8)

  • CSV/JSON result exports → F8.2
  • Cross-surface anonymous-mode PII audit → F8.3

🤖 Generated with Claude Code

Add the admin read-side view of completed-session data, scoped to a
questionnaire version: per-question answer distributions, an invitation
completion funnel, and per-version cost actuals — with tag-aware filtering.
Read-only and aggregate-only (free-text answer values are never surfaced),
so the surface is PII-safe ahead of F8.3 anonymous-mode hardening.

- Core aggregators (pure, Prisma at the seam) in lib/app/questionnaire/analytics:
  distributions, funnel, cost (+ shared query schema / scope resolver).
- Three admin-only, flag-gated, version-scoped GET routes under
  .../versions/[vid]/analytics/{distributions,funnel,cost}.
- Version-scoped Analytics tab + client UI (filter, distribution, funnel,
  cost panels); recharts for the cost trend.
- Cost attribution: standardise the four session-bound capabilities on the
  canonical AiCostLog metadata key `appQuestionnaireSessionId` (runtime spend),
  with design-time spend read via `metadata.versionId`. No migration.

Tests: 45 unit (aggregator math, per-type distributions, funnel staging,
cost runtime/design split + edge branches) + 21 route integration tests
(auth, version-scope, validation, response shape). Docs + F8.1 tracker added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JohnD-EE JohnD-EE merged commit b99e7d5 into main Jun 8, 2026
14 checks passed
@JohnD-EE JohnD-EE deleted the feat/F8.1-admin-analytics-dashboards branch June 8, 2026 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant